import math
from collections import OrderedDict
from functools import partial
from multiprocessing import Queue

from PySide6.QtCore import Signal, Qt
from PySide6.QtGui import QAction, QColor
from PySide6.QtWidgets import QTableWidget, QTableWidgetItem, QAbstractItemView, QMenu, QPushButton, QHeaderView

from extend_ui.dialog_class import Dialog_Edit, Dialog_Combox, Dialog_Table
from extend_ui.err_log import log_decorator


class Table_Base(QTableWidget):
    # 用于显示的列
    column = {}

    def __init__(self,
                 parent=None,
                 signal_table_info=Signal(),
                 table_info=None):
        super(Table_Base, self).__init__(parent)
        # 获取变量
        self.table_info = table_info
        self.signal_table_info = signal_table_info
        self.para_list = list(self.column.keys())

        # 创建变量
        self.para_addrs = []        # 存储地址但不显示
        self.para_tips = []         # 存储变量说明，用于提示
        # 存储用于显示到table页面的数据
        self.para_dict = {}
        for key, value in self.column.items():
            self.para_dict[key] = []

        # 获取数据
        self.get_row()
        # 设置表格参数
        self.set_table()
        # 创建条目
        self.init_row()

    def get_row(self):
        """ 从 table_info 中 获取行数据 """
        pass

    def set_table(self):
        """ 配置表格 """
        self.setRowCount(len(self.para_addrs))  # 设置行数
        self.setColumnCount(len(self.column))  # 设置列数
        self.verticalHeader().setVisible(False)  # 设置表头状态
        self.setHorizontalHeaderLabels(list(self.column.values()))  # 设置列名
        header = self.horizontalHeader()                            # 获得列头
        header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)  # 设置自动列宽
        header.setMinimumSectionSize(110)                                     # 设置最小列宽
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)  # 禁止编辑
        self.setSelectionBehavior(QAbstractItemView.SelectRows)  # 设置选择行为为单行
        self.cellDoubleClicked.connect(self.double_clicked)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.right_clicked)

    def init_row(self):
        """ 添加数据 """
        for i in range(len(self.para_dict[self.para_list[0]])):
            for j in range(len(self.column)):
                # 添加特殊类型数据
                if self.para_list[j] == 'button':
                    self.setCellWidget(i, j, self.para_dict[self.para_list[j]][i])
                else:
                    table_item = QTableWidgetItem(self.para_dict[self.para_list[j]][i])
                    table_item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
                    self.setItem(i, j, table_item)
                    if '变量名' in list(self.column.values())[j] and self.para_tips[i]:
                        table_item.setToolTip(self.para_tips[i])

    @log_decorator
    def double_clicked(self, row, col):
        """ 表格的双击行为 """
        if self.para_list[col] == 'var_current':
            para_addr = self.para_addrs[row]
            if self.table_info['var_type'][para_addr] in ['5', 'bit'] and self.table_info['action_define'][para_addr]:
                dialog_table = Dialog_Table(self)
                dialog_table.display(para_addr)
            elif self.table_info['var_type'][para_addr] in ['3', 'enum']  and self.table_info['action_define'][para_addr]:
                dialog_combox = Dialog_Combox(self)
                dialog_combox.display(para_addr)
            else:
                dialog_edit = Dialog_Edit(self)
                dialog_edit.display(para_addr)

    def right_clicked(self, pos):
        """ 表格的右键行为 """
        pass

    def refresh(self):
        """ 刷新当前值 """
        if self.isVisible():
            for i in range(len(self.para_addrs)):
                addr = self.para_addrs[i]
                col_curr_value = self.para_list.index('var_current')
                curr_value = self.table_info['var_current'][addr]
                if self.table_info['var_type'][addr] in ['bit', '5']:
                    self.item(i, col_curr_value).setText(hex(int(curr_value)))
                elif self.table_info['var_type'][addr] in ['enum', '3'] and self.table_info['action_define'][addr]:
                    if str(int(curr_value)) in self.table_info['action_define'][addr].keys():
                        self.item(i, col_curr_value).setText(self.table_info['action_define'][addr][str(int(curr_value))])
                    else:
                        self.item(i, col_curr_value).setText(str(int(curr_value)))
                elif self.table_info['var_type'][addr] in ['int', '1']:
                    self.item(i, col_curr_value).setText(str(curr_value) + self.table_info['var_unit'][addr])
                else:
                    if 0.0 < abs(curr_value) < 0.01:
                        self.item(i, col_curr_value).setText(
                            '{:.2e}'.format(curr_value) + self.table_info['var_unit'][addr])
                    elif curr_value > 100000:
                        self.item(i, col_curr_value).setText(
                            '{:.4e}'.format(curr_value) + self.table_info['var_unit'][addr])
                    else:
                        if self.table_info['var_decimal'][addr]:
                            self.item(i, col_curr_value).setText(
                                ('%.' + self.table_info['var_decimal'][addr] + 'f' % curr_value) +
                                self.table_info['var_unit'][
                                    addr])
                        else:
                            self.item(i, col_curr_value).setText(
                                ('%.2f' % curr_value) + self.table_info['var_unit'][addr])


class Table_Center(Table_Base):
    # 用于显示的列
    column = {
        'var_name': '变量名',
        'var_current': '当前值(单位)',
        'var_type': '类型'
    }

    def __init__(self,
                 parent=None,
                 table_name=None,
                 signal_table_info=Signal(),
                 signal_clear=Signal(),
                 table_info=None,
                 queue_logging=Queue()):
        # 获取变量
        self.table_name = table_name
        self.signal_clear = signal_clear
        self.queue_logging = queue_logging
        super(Table_Center, self).__init__(parent,
                                           signal_table_info=signal_table_info,
                                           table_info=table_info)

    def get_row(self):
        """ 根据需求获取想要的数据 """
        for i in range(len(self.table_info['type_lv2'])):
            if self.table_name == self.table_info['category_id'][i] + '-' + self.table_info['type_lv2'][i]:
                self.para_addrs.append(self.table_info['addr'][i])
                self.para_tips.append(self.table_info['var_comment'][i])
                self.para_dict['var_name'].append(self.table_info['var_name'][i])
                if self.table_info['var_current'][i] == '':
                    if self.table_info['var_type'][i] in ['3', 'enum']:
                        if not self.table_info['action_define']:
                            self.para_dict['var_current'].append(str(0))
                        else:
                            self.para_dict['var_current'].append(list(self.table_info['action_define'][i].values())[0])
                    elif self.table_info['var_type'][i] in ['5', 'bit']:
                        self.para_dict['var_current'].append('0x0')
                    elif self.table_info['var_type'][i] in ['1', 'int']:
                        self.para_dict['var_current'].append(str(0) + self.table_info['var_unit'][i])
                    else:
                        self.para_dict['var_current'].append(str(0.0) + self.table_info['var_unit'][i])
                else:
                    if self.table_info['var_type'][i] in ['3', 'enum']:
                        if not self.table_info['action_define'][i]:
                            self.para_dict['var_current'].append(str(0))
                        else:
                            self.para_dict['var_current'].append(
                                self.table_info['action_define'][i][str(int(self.table_info['var_current'][i]))])
                    elif self.table_info['var_type'][i] in ['5', 'bit']:
                        if not self.table_info['action_define'][i]:
                            self.para_dict['var_current'].append('0x0')
                        else:
                            self.para_dict['var_current'].append(hex(int(self.table_info['var_current'][i])))
                    elif self.table_info['var_type'][i] in ['1', 'int']:
                        if self.table_info['var_unit'][i]:
                            self.para_dict['var_current'].append(
                                str(int(self.table_info['var_current'][i])) + self.table_info['var_unit'][i])
                        else:
                            self.para_dict['var_current'].append(str(int(self.table_info['var_current'][i])))
                    else:
                        if self.table_info['var_unit'][i]:
                            self.para_dict['var_current'].append(
                                str(self.table_info['var_current'][i]) + self.table_info['var_unit'][i])
                        else:
                            self.para_dict['var_current'].append(str(self.table_info['var_current'][i]))
                if self.table_info['isWritable'][i] == 1:
                    self.para_dict['var_type'].append('R/W')
                else:
                    self.para_dict['var_type'].append('Ro')

    def double_clicked(self, row, col):
        super().double_clicked(row, col)
        if self.para_list[col] == 'var_name':
            para_addr = self.para_addrs[self.currentItem().row()]
            if self.table_info['var_type'][para_addr] in ['5', 'bit']:
                if self.table_info['action_define'][para_addr]:
                    self.signal_table_info.emit(3, [para_addr], [None])
                else:
                    self.signal_table_info.emit(5, [para_addr], [None])
            elif self.table_info['var_type'][para_addr] in ['3', 'enum']:
                if not self.table_info['action_define'][para_addr]:
                    self.signal_table_info.emit(5, [para_addr], [None])
                elif list(self.table_info['action_define'][para_addr].values())[0] == '禁止' or \
                        list(self.table_info['action_define'][para_addr].values())[0] == 'off' or \
                        list(self.table_info['action_define'][para_addr].values())[0] == 'no':
                    self.signal_table_info.emit(3, [para_addr], [None])
                else:
                    self.signal_table_info.emit(3, [para_addr], [None])
            else:
                self.signal_table_info.emit(5, [para_addr], [None])

    @log_decorator
    def right_clicked(self, pos):
        """ 表格的右键行为 """
        item = self.itemAt(pos)
        # 判断右键处于表格上还是空白处
        if item is None:
            action = QAction("Delete ALL", self)
            action.triggered.connect(self.delete_all_table)
            menu = QMenu(self)
            menu.addAction(action)
            menu.exec(self.mapToGlobal(pos))
        else:
            if self.currentItem().column() == self.para_list.index('var_name'):
                para_addr = self.para_addrs[self.currentItem().row()]
                menu_button = QMenu(self)
                menu_pull_button = QMenu(self)
                menu_button_group = QMenu(self)
                menu_textline = QMenu(self)
                add_button_on_off = menu_button.addAction('添加按钮')
                add_combox_on_off = menu_button.addAction('添加单选框')
                add_pull_button = menu_pull_button.addAction('添加按钮')
                add_pull_combox = menu_pull_button.addAction('添加单选框')
                add_buttons = menu_button_group.addAction('添加一组按钮')
                add_textline = menu_textline.addAction('添加输入框')
                # 弹出对应菜单
                if self.table_info['var_type'][para_addr] in ['5', 'bit']:
                    if self.table_info['action_define'][para_addr]:
                        action = menu_button_group.exec_(self.mapToGlobal(pos))
                    else:
                        action = menu_textline.exec_(self.mapToGlobal(pos))
                elif self.table_info['var_type'][para_addr] in ['3', 'enum']:
                    if not self.table_info['action_define'][para_addr]:
                        action = menu_textline.exec_(self.mapToGlobal(pos))
                    elif list(self.table_info['action_define'][para_addr].values())[0] == '禁止' or \
                            list(self.table_info['action_define'][para_addr].values())[0] == 'off' or \
                            list(self.table_info['action_define'][para_addr].values())[0] == 'no':
                        action = menu_button.exec_(self.mapToGlobal(pos))
                    else:
                        action = menu_pull_button.exec_(self.mapToGlobal(pos))
                else:
                    action = menu_textline.exec_(self.mapToGlobal(pos))

                # 点击菜单作出响应判断
                if action == add_buttons:
                    self.signal_table_info.emit(3, [para_addr], [None])
                elif action == add_button_on_off:
                    self.signal_table_info.emit(3, [para_addr], [None])
                elif action == add_combox_on_off:
                    self.signal_table_info.emit(4, [para_addr], [None])
                elif action == add_pull_button:
                    self.signal_table_info.emit(3, [para_addr], [None])
                elif action == add_pull_combox:
                    self.signal_table_info.emit(4, [para_addr], [None])
                elif action == add_textline:
                    self.signal_table_info.emit(5, [para_addr], [None])
            else:
                pass

    @log_decorator
    def delete_all_table(self):
        """ 清除所有tab """
        self.signal_clear.emit()


class Table_E2prom(Table_Base):
    # 用于显示的列
    column = {
        'type_lv2': '类型',
        'uniqueID': '变量名(唯一标识)',
        'var_current': '当前值(单位)',
        'var_default': '缺省值',
        'button': '保存到E2PROM'
    }

    def __init__(self,
                 parent=None,
                 signal_table_info=Signal(),
                 table_info=None,
                 signal_button_alone_save=Signal()):
        super(Table_E2prom, self).__init__(parent,
                                           signal_table_info=signal_table_info,
                                           table_info=table_info,)
        # 获取变量
        self.signal_button_alone_save = signal_button_alone_save
        self.queue_logging = parent.queue_logging

    def get_row(self):
        """ 从 table_info 获取想要显示的数据 """
        for i in range(len(self.table_info['isEEPROM'])):
            if self.table_info['isEEPROM'][i] == 1:
                self.para_addrs.append(self.table_info['addr'][i])
                self.para_tips.append(self.table_info['var_comment'][i])
                self.para_dict['type_lv2'].append(self.table_info['type_lv1'][i] + '-' + self.table_info['type_lv2'][i])
                self.para_dict['uniqueID'].append(self.table_info['uniqueID'][i])
                if self.table_info['var_current'][i] == '':
                    if self.table_info['var_type'][i] in ['3', 'enum']:
                        if not self.table_info['action_define']:
                            self.para_dict['var_current'].append(str(0))
                        else:
                            self.para_dict['var_current'].append(list(self.table_info['action_define'][i].values())[0])
                    elif self.table_info['var_type'][i] in ['5', 'bit']:
                        self.para_dict['var_current'].append('0x0')
                    elif self.table_info['var_type'][i] in ['1', 'int']:
                        self.para_dict['var_current'].append(str(0) + self.table_info['var_unit'][i])
                    else:
                        self.para_dict['var_current'].append(str(0.0) + self.table_info['var_unit'][i])
                else:
                    if self.table_info['var_type'][i] in ['3', 'enum']:
                        if not self.table_info['action_define']:
                            self.para_dict['var_current'].append(str(0))
                        else:
                            self.para_dict['var_current'].append(
                                self.table_info['action_define'][i][str(self.table_info['var_current'][i])])
                    elif self.table_info['var_type'][i] in ['5', 'bit']:
                        if not self.table_info['action_define'][i]:
                            self.para_dict['var_current'].append('0x0')
                        else:
                            self.para_dict['var_current'].append(hex(int(self.table_info['var_current'][i])))
                    elif self.table_info['var_type'][i] in ['1', 'int']:
                        if self.table_info['var_unit'][i]:
                            self.para_dict['var_current'].append(str(int(self.table_info['var_current'][i])) + self.table_info['var_unit'][i])
                        else:
                            self.para_dict['var_current'].append(str(int(self.table_info['var_current'][i])))
                    else:
                        if self.table_info['var_unit'][i]:
                            self.para_dict['var_current'].append(str(self.table_info['var_current'][i]) + self.table_info['var_unit'][i])
                        else:
                            self.para_dict['var_current'].append(str(self.table_info['var_current'][i]))

                if self.table_info['var_default'][i]:
                    if self.table_info['var_type'][i] in ['3', 'enum']:
                        if not self.table_info['action_define'][i]:
                            self.para_dict['var_default'].append(str(self.table_info['var_default'][i]))
                        else:
                            self.para_dict['var_default'].append(
                                self.table_info['action_define'][i][self.table_info['var_default'][i]])
                    elif self.table_info['var_type'][i] in ['5', 'bit']:
                        self.para_dict['var_default'].append(hex(int(float(self.table_info['var_default'[i]]))))
                    else:
                        if 0.0 < abs(self.table_info['var_default'][i]) < 0.01:
                            self.para_dict['var_default'].append('{:.2e}'.format(self.table_info['var_default'][i]))
                        elif self.table_info['var_default'][i] > 100000:
                            self.para_dict['var_default'].append('{:.4e}'.format(self.table_info['var_default'][i]))
                        else:
                            if self.table_info['var_decimal'][i]:
                                self.para_dict['var_default'].append(
                                    ('%.' + str(self.table_info['var_decimal'][i]) + 'f' % self.table_info['var_default'][i]) + self.table_info['var_unit'][i])
                            else:
                                self.para_dict['var_default'].append(
                                    ('%.2f' % self.table_info['var_default'][i]) + self.table_info['var_unit'][i])
                else:
                    if self.table_info['var_type'][i] in ['3', 'enum']:
                        if not self.table_info['action_define'][i]:
                            self.para_dict['var_default'].append('0')
                        else:
                            self.para_dict['var_default'].append(list(self.table_info['action_define'][i].values())[0])
                    elif self.table_info['var_type'][i] in ['5', 'bit']:
                        if not self.table_info['action_define'][i]:
                            self.para_dict['var_default'].append('0x0')
                        else:
                            self.para_dict['var_default'].append('0x0')
                    else:
                        self.para_dict['var_default'].append('0.0')
                button = QPushButton('保存')
                button.clicked.connect(partial(self.button_alone_save_slot, para_adr=self.table_info['addr'][i]))
                self.para_dict['button'].append(button)

    @log_decorator
    def button_alone_save_slot(self, para_adr):
        """ 每个保存按钮的请求 """
        self.signal_button_alone_save.emit(para_adr)

    def update_default(self):
        for i in range(len(self.table_info['var_current'])):
            if self.table_info['isEEPROM'][i] == 1:
                self.table_info['var_default'][i] = self.table_info['var_current'][i]
        col_default = self.para_list.index('var_default')
        for i in range(self.rowCount()):
            if self.table_info['var_type'][i] in ['5', 'bit']:
                if not self.table_info['action_define'][i]:
                    self.item(i, col_default).setText(str(hex(int(float(self.table_info['var_current'][i])))))
                elif self.table_info['var_current'][i] == 0:
                    self.item(i, col_default).setText('0x0')
                else:
                    self.item(i, col_default).setText(self.table_info['action_define'][i][
                                                            int(math.log(int(self.table_info['var_current'][i]), 2))])
            elif self.table_info['var_type'][i] in ['3', 'enum']:
                if not self.table_info['action_define'][i]:
                    self.item(i, col_default).setText(str(self.table_info['var_current'][i]))
                else:
                    self.item(i, col_default).setText(
                        self.table_info['action_define'][i][int(self.table_info['var_current'][i])])
            else:
                # self.table_widget.item(i, 4).setText(str(self.table_info['var_current'][i]))

                if 0.0 < abs(self.table_info['var_current'][i]) < 0.01:
                    self.item(i, col_default).setText('{:.2e}'.format(self.table_info['var_current'][i]))
                elif self.table_info['var_current'][i] > 100000:
                    self.item(i, col_default).setText('{:.4e}'.format(self.table_info['var_current'][i]))
                else:
                    if self.table_info['var_decimal'][i]:
                        self.item(i, col_default).setText(
                            ('%.' + self.table_info['var_decimal'][i] + 'f' % self.table_info['var_current'][i]))
                    else:
                        self.item(i, col_default).setText(
                            ('%.2f' % self.table_info['var_current'][i]) + self.table_info['var_unit'][i])

    def update_default_one(self, addr):
        if addr not in self.para_addrs:
            return
        col_default = self.para_list.index('var_default')
        if self.table_info['var_type'][addr] in ['5', 'bit']:
            if not self.table_info['action_define'][addr]:
                self.item(addr, col_default).setText(str(hex(int(float(self.table_info['var_default'][addr])))))
            elif self.table_info['var_default'][addr] == 0:
                self.item(addr, col_default).setText('0x0')
            else:
                self.item(addr, col_default).setText(self.table_info['action_define'][addr][
                                                      int(math.log(int(self.table_info['var_default'][addr]), 2))])
        elif self.table_info['var_type'][addr] in ['3', 'enum']:
            if not self.table_info['action_define'][addr]:
                self.item(addr, col_default).setText(str(self.table_info['var_default'][addr]))
            else:
                self.item(addr, col_default).setText(
                    self.table_info['action_define'][addr][int(self.table_info['var_default'][addr])])
        else:
            # self.table_widget.item(i, 4).setText(str(self.table_info['var_current'][i]))

            if 0.0 < abs(self.table_info['var_default'][addr]) < 0.01:
                self.item(addr, col_default).setText('{:.2e}'.format(self.table_info['var_default'][addr]))
            elif self.table_info['var_default'][addr] > 100000:
                self.item(addr, col_default).setText('{:.4e}'.format(self.table_info['var_default'][addr]))
            else:
                if self.table_info['var_decimal'][addr]:
                    self.item(addr, col_default).setText(
                        ('%.' + self.table_info['var_decimal'][addr] + 'f' % self.table_info['var_default'][addr]))
                else:
                    self.item(addr, col_default).setText(
                        ('%.2f' % self.table_info['var_default'][addr]) + self.table_info['var_unit'][addr])

    def init_row(self):
        # 给背景区分颜色
        super(Table_E2prom, self).init_row()
        type_name_list = list(OrderedDict.fromkeys(self.para_dict['type_lv2']))
        # type_name_list = list(set(self.para_dict['type_lv2']))
        for i in range(self.rowCount()):
            if type_name_list.index(self.item(i, 0).text()) % 2 == 0:
                self.setRowColor(i, QColor(240, 240, 240, 255))     # 灰色
            else:
                pass

    def setRowColor(self, row, color):
        """ 为每一行的item 设置颜色， -1 是不包括按钮 """
        for col in range(self.columnCount()-1):
            item = self.item(row, col)
            item.setBackground(color)

